HTML 5 Game Development - Tutorial step 1


Lately I've been wondering what we'll see after GWT, the next Big Thing. I've tried a couple of JavaScript framework, like Angular.js and Ember.js because I want back the "change something, hit refresh to test it" of the old days

While they look promising (and finally some structure over big JS application) they are still linked to Javascript; I wanted to try something new. Since Google has just released Dart as a stable 1.0 version (link) it was worth giving it a try and what's better than that to write an HTML5 game?

Mind you, I'm a newbie of both Dart and HTML5 gaming so no guarantee at all that this is the right thing to do. This is just a recollection mainly for me, but it might be useful for other people, so publishing it here

First, let's take a look at the "finished game"; nothing "cool", just avoid the monster going around (WASD on the keyboard) until its time is over or you're dead

Live Demo

The tutorial is structured as steps, each one will take us closer to the final game.

Let's start off with a very basic HTML page with a canvas and the hook-up for our Dart code

<img id="loading" src="images/loading.gif" alt="Loading..."></img> <canvas id="game_canvas" width="600px" height="400px" class="invisible"> </canvas> <footer> <p id="fps"></p> </footer> <script type="application/dart" src="html5_learning.dart"></script> <script src="packages/browser/dart.js"></script>

Ok. So when you'll load the application the browser will call the html5_learning.dart which is very basic. It'll just call the real game that will be built progressively (step2/game = step1/game + something more added, etc)

import 'dart_01/game.dart';

void main() {
  //launch the game
  HTML5Game game = new HTML5Game();
  game.startGame();
}

Let's add some Classes that will represent the world information, the Player and the Monster. Both classes extend Character (simply the position relative to top left corner of the canvas and the image to draw to represent them to the user).

part of html5Game;

/**
 * Base class for the Characters of the game.
 */
abstract class Character {
  int x = 0;
  int y = 0;
  ImageElement charImage;
  
  Character() {
    x = r.nextInt(WIDTH);
    y = r.nextInt(HEIGHT);
  }
  
  void draw(CanvasRenderingContext2D context) {
    context.drawImage(charImage, x, y);
  }
}

The Player has a health that will be decreased every time he gets touched by the monster, while the monster needs to kill the Player before its time to live expires.

/**
 * A Player of the Game.
 * Must remain alive!!!
 */
class Player extends Character {
  int health = 100;
  
  //you've been hit
  void decrementHealth() {
    health--;
  }
}

/**
 * A Monster of the Game.
 * It has to kill the poor Player...you!
 */
class Monster extends Character {
  //time to live
  int ttl=10;
  
  void decrementTimeToLive() {
    ttl--;
  }
}

If you're familiar with any OO programming language you'll be familiar with this code. Just a couple of pointers:

  1. 1 file = multiple Class definitions. No need (but you can) for a 1 to 1 relation
  2. Setter and getter defined by default - no need to add them
  3. I'll import this file in the "main" class for the game but I have to specify that this will be a part of that library/application


In the game.dart we will do most of the job. We will need to:
  1. get a reference to the Canvas defined in the HTML file to be able to draw on it
  2. load all the asset used during the game - in this case the images for the background and the two Character (the Player and the Monster)
  3. game "logic" - it update the world information (player/monster position, health check etc) and draws the updated state

Point 1 is done inside the constructor as I've defined a Class HTML5Game which will contain the world state (the two characters) and a reference to the Canvas

part 'entities.dart';

/**
 * Main Class 
 */
class HTML5Game {
  Player p;
  Monster m;
  CanvasElement canvas;
  ImageElement background;
  
  HTML5Game() {
    p = new Player();
    m = new Monster();
    this.canvas = querySelector("#game_canvas");;
  }
  //
}

Loading the images is done by defining 3 ImageElement with the source link of the image to use (see Credit section at the end).

First problem: the browser will download the Images for us but we need to get a "callback" to know when they are all "ready" so that we can use them.

With the Future feature in Dart this is quite easy; just create an array of image.onLoad to get a Future for each image. Then define a Future.wait on the array to get a callback once all the Future are ready (the images have been downloaded and can be used). I love Futures and Dart has them built in since the start.

You might wonder the .first in the future definition. We'll see it in more detail once we need to get the keyboard input from the user

  /**
   * Method to start the game.
   * Its responsabilities are:
   * 1) to initialize the UI
   * 2) start the main loop
   */
  startGame() {
    //get Element from DOM
    Element loading = querySelector("#loading");
    
    //load background
    background = new ImageElement(src: "images/background.png");
    ImageElement hero = new ImageElement(src: "images/hero.png");
    ImageElement monster = new ImageElement(src: "images/monster.png");
    
    var futures = [background.onLoad.first, hero.onLoad.first, monster.onLoad.first];
    
    Future.wait(futures).then((_) {
      print("loaded all images");
      
      //draw background
      canvas.className = "";
      loading.classes.add("invisible");
      canvas.context2D.drawImageScaled(background, 0, 0, WIDTH, HEIGHT);
      
      //hero and monster
      p.charImage = hero;
      m.charImage = monster;
      
      mainGame();
    });
  }

Ok, so now we've started the mainGame loop function. This will:
  1. Update the world; in the first step of the tutorial, we just pick random values inside the canvas for the Player and the Monster. It'll look like they are just popping out of nowhere but it's just the first step
  2. Render the world; it'll clean everything in the canvas, draw the background from the top left corner (0,0), draw the characters and schedule the next loop
  /**
   * Game loop.
   * Each loop:
   * 1) the game will calculate the new properties for the characters of the game (position, ammo, health etc)
   * 2) draw the update value
   */
  mainGame() {
    update();
    renderWorld();
  }
  
  update() {
    //for now randomly move people around
    p.x = r.nextInt(WIDTH);
    p.y = r.nextInt(HEIGHT);
    
    m.x = r.nextInt(WIDTH);
    m.y = r.nextInt(HEIGHT);
  }
  
  renderWorld() {
    window.requestAnimationFrame(draw);
  }

  void draw(num _) {
    //clean everything
    canvas.context2D.clearRect(0, 0, WIDTH, HEIGHT);
    
    //reprint background
    canvas.context2D.drawImageScaled(background, 0, 0, WIDTH, HEIGHT);
    p.draw(canvas.context2D);
    m.draw(canvas.context2D);
    
    //next game loop
    mainGame();
  }


This is the resulting game after the first 1.

Demo

Not exciting, right? Let's go to step2

Credit:

Characters: http://untamed.wild-refuge.net/rmxpresources.php?characters

Background: http://opengameart.org/node/7919



Theme Sponsored by: Roller Blinds, Cyprus Holidays, Walk in Baths